/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package SintaticoPacote;


import SintaticoPacote.TratamentoErro.ErroSintaticoInterface;
import SintaticoPacote.TratamentoErro.ErroTabela;
import SintaticoPacote.TratamentoErro.TabelaErros;
import compilador_full.TabelaSimbolos;
import java.util.ArrayList;

/**
 *
 * @author Igo
 */
public class declaracaoMetodos implements ErroSintaticoInterface{


    ArrayList<String> sincronizadores = new ArrayList<String>();
    ArrayList<String> sincronizadoresDeclaracoes = new ArrayList<String>();
    ArrayList<String> sincronizadoresCmd = new ArrayList<String>();


    public declaracaoMetodos() {
        carregaSincronizadores();
        carregaSincronizadoresDeclaracoes();
        carregaSincronizadoresCmd();
    }


    public void carregaSincronizadores(){
        sincronizadores.add("int");
        sincronizadores.add("float");
        sincronizadores.add("char");
        sincronizadores.add("boolean");
        sincronizadores.add("void");
        sincronizadores.add("identificador");
        sincronizadores.add("do");
        sincronizadores.add("printf");
        sincronizadores.add("while");
        sincronizadores.add("for");
        sincronizadores.add("scanf");
        sincronizadores.add("*");
        sincronizadores.add("if");
        sincronizadores.add("{");

    }

        public void carregaSincronizadoresDeclaracoes(){
        sincronizadoresDeclaracoes.add("typedef");
        sincronizadoresDeclaracoes.add("int");
        sincronizadoresDeclaracoes.add("float");
        sincronizadoresDeclaracoes.add("char");
        sincronizadoresDeclaracoes.add("boolean");
        sincronizadoresDeclaracoes.add("struct");
        sincronizadoresDeclaracoes.add("identificador");
        sincronizadoresDeclaracoes.add("do");
        sincronizadoresDeclaracoes.add("printf");
        sincronizadoresDeclaracoes.add("while");
        sincronizadoresDeclaracoes.add("for");
        sincronizadoresDeclaracoes.add("scanf");
        sincronizadoresDeclaracoes.add("*");
        sincronizadoresDeclaracoes.add("if");
        sincronizadoresDeclaracoes.add("return");
        sincronizadoresDeclaracoes.add("void");
    }


        public void carregaSincronizadoresCmd(){
        sincronizadoresDeclaracoes.add("identificador");
        sincronizadoresDeclaracoes.add("do");
        sincronizadoresDeclaracoes.add("printf");
        sincronizadoresDeclaracoes.add("while");
        sincronizadoresDeclaracoes.add("for");
        sincronizadoresDeclaracoes.add("scanf");
        sincronizadoresDeclaracoes.add("*");
        sincronizadoresDeclaracoes.add("if");
        sincronizadoresDeclaracoes.add("return");
        sincronizadoresDeclaracoes.add("void");
    }





    public boolean declaraMetodos(TabelaSimbolos tabelaSimbolos){

        parametros verificaParametros = new parametros();
        declaracaoStructs verificaDeclaracaoStruct = new declaracaoStructs(tabelaSimbolos);
        declaracaoVariaveis verificaDeclaracaoVariaveis = new declaracaoVariaveis(tabelaSimbolos);
        declaracaoPonteiros verificaDeclaracaoPonteiros = new declaracaoPonteiros(tabelaSimbolos);
        cmd verificaCmd = new cmd();
        chamadaDeFuncao verificaChamadaDeFuncao = new chamadaDeFuncao(tabelaSimbolos);



        if(tabelaSimbolos.getTipo().equals("palavra") || tabelaSimbolos.getTipo().equals("identificador")) {

            tabelaSimbolos.salvaTipo(tabelaSimbolos.getLookahead());
            tabelaSimbolos.soConsome();
            tabelaSimbolos.insertEscopo();


            if(tabelaSimbolos.getLookahead().equals("*"))
                tabelaSimbolos.reconhecer("*");
            else if(tabelaSimbolos.getTipo().equals("identificador")) {

                // salva o nome do escopo
                tabelaSimbolos.salvaEscopo(tabelaSimbolos.getLookahead());
                // salva o ponteiro
                tabelaSimbolos.salvaPonteiroParametros();

                // salva escopo
                tabelaSimbolos.salvaEscopo2(tabelaSimbolos.getLookahead());

                tabelaSimbolos.insertCategoria("metodo");
                tabelaSimbolos.insertTipoVariavel();


                tabelaSimbolos.soConsome();   //Reconhece o identificador
                if(tabelaSimbolos.reconhecer("("))  //Reconhece o parenteses abrindo (
                    if(!tabelaSimbolos.getLookahead().equals(")")) {
                        if(verificaParametros.sintaxeParametros(tabelaSimbolos)) { }    //Retorna o ponteiro sobre um ")"
                        else return false;
                    }

                    if(!tabelaSimbolos.reconhecer(")"))
                        this.recuperaErro(tabelaSimbolos.getLinha(), tabelaSimbolos, "Erro: ) esperado na linha: ", sincronizadores);
                    else if(!tabelaSimbolos.reconhecer("{"))
                        this.recuperaErro(tabelaSimbolos.getLinha(), tabelaSimbolos, " Erro: { esperado na linha: ", sincronizadores);

                TabelaErros.getInstance().imprimeTabela();

                    while (tabelaSimbolos.getLookahead().equals("typedef") ||
                            tabelaSimbolos.getLookahead().equals("struct") ||
                            Tipos.apenasCompara(tabelaSimbolos)) {

                        //tabelasimbolos.reconhecer(tabelasimbolos.getLookahead());
                        //Pode ter um typedef ou não, por isso foi usado esse if, caso tenha,
                        //e o else if seguinte, caso não tenha, venha direto o struct
                        if (tabelaSimbolos.getLookahead().equals("typedef")) {
                            if (tabelaSimbolos.superLookahead(1).equals("struct")) {

                                verificaDeclaracaoStruct.declaraStructs();
                            }
                            else if (Tipos.apenasCompara(tabelaSimbolos)) {
                                verificaDeclaracaoVariaveis.declaraVariaveis();
                            }
                            else
                                this.recuperaErro(0, tabelaSimbolos, "Erro: estrutura typedef mal formada  na linha: ", sincronizadoresDeclaracoes);

                        }

                        else if (tabelaSimbolos.getLookahead().equals("struct")) {
                            verificaDeclaracaoStruct.declaraStructs();
                        }

                        else if (Tipos.apenasCompara(tabelaSimbolos)) {
                            if(tabelaSimbolos.getTipo().equals("identificador"))
                            {
                                tabelaSimbolos.soConsome();//Consome identificador
                                tabelaSimbolos.soConsome();//Consome o =
                                tabelaSimbolos.soConsome();//Consome o que vem depois do igual
                                tabelaSimbolos.soConsome();//Consome o ;

                            }
                            else if (tabelaSimbolos.superTipo(1).equals("identificador")) {
                                verificaDeclaracaoVariaveis.declaraVariaveis();
                            } else if (tabelaSimbolos.superLookahead(1).equals("*"))
                                verificaDeclaracaoPonteiros.declaraPonteiros();
                            else
                                this.recuperaErro(0, tabelaSimbolos, "Erro: estrutura typedef mal formada  na linha: ", sincronizadoresDeclaracoes);
                        }

                        //esse else foi retirado pois ele impossibilitava do fecho: {<declaracaoStructs> | <declaracaoVariaveis> | <declaracaoPonteiros>}
                        // ser vazio
                        //else
                        //return false;
                    }

                    //while que representa essa fecho: {<declaracaoStructs> | <declaracaoVariaveis> | <declaracaoPonteiros>}
                    // Aqui acabou o primeiro fecho, agora vai ser feito o fecho do cmd:
                    // {<cmd>}
                    while (tabelaSimbolos.getLookahead().equals("*") |
                            tabelaSimbolos.getLookahead().equals("printf") |
                            tabelaSimbolos.getLookahead().equals("scanf") |
                            tabelaSimbolos.getLookahead().equals("do") |
                            tabelaSimbolos.getLookahead().equals("while") |
                            tabelaSimbolos.getLookahead().equals("for") |
                            tabelaSimbolos.getLookahead().equals("if") |
                            tabelaSimbolos.getTipo().equals("identificador")) {

                        verificaCmd.sintaxeCmd(tabelaSimbolos);
                    }

                    if(tabelaSimbolos.reconhecer("return")) {

                        System.out.println(tabelaSimbolos.getLookahead());
                        System.out.println(tabelaSimbolos.getTipo());
                        System.out.println(tabelaSimbolos.superLookahead(1));
                        System.out.println(tabelaSimbolos.superTipo(1));


                       if(tabelaSimbolos.getTipo().equals("numero"))
                           tabelaSimbolos.reconhecer(tabelaSimbolos.getLookahead());

                       //COnsertar. Aqui tem que retornar verdadeiro se o elemento for um boolean
                       else if(tabelaSimbolos.getLookahead().equals("true") | tabelaSimbolos.getLookahead().equals("false"))
                           tabelaSimbolos.reconhecer(tabelaSimbolos.getLookahead());

                       //Nosso léxico não classifica como caractere.
                       //Caso venha um return 'a', esse 'a' ele considera como identificador, logo
                       //essa condição ja esta sendo atendida no else if seguinte.
                       else if(tabelaSimbolos.getTipo().equals("frase")){
                           // Consertar. Tem que retornar verdadeiro se o elemento for um caracter
                           tabelaSimbolos.soConsome();
                       }

                       else if(tabelaSimbolos.getTipo().equals("identificador") &&
                                tabelaSimbolos.superLookahead(1).equals(";")) {
                           tabelaSimbolos.reconhecer(tabelaSimbolos.getLookahead());
                       }

                       else if(tabelaSimbolos.getTipo().equals("numero") ||
                               tabelaSimbolos.getTipo().equals("identificador") ||
                               tabelaSimbolos.getLookahead().equals("(") ){

                           if(verificaChamadaDeFuncao.chamaFuncao()){

                           }
                           else
                               return false;
                       }
                       else
                        {
                            ErroTabela erro = new ErroTabela(tabelaSimbolos.getLinha(), "Falta ou um número ou um identificador ou um (");
                            TabelaErros.getInstance().getTabelaErros().add(erro);// Tabela de Erros ja contem o erro. Isso que será impresso na tela.
                            return false;
                        }

                       if(tabelaSimbolos.getLookahead().equals(";")){
                           tabelaSimbolos.reconhecer(";");
                       }
                       else
                        {
                            ErroTabela erro = new ErroTabela(tabelaSimbolos.getLinha(), "Falta um ;");
                            TabelaErros.getInstance().getTabelaErros().add(erro);// Tabela de Erros ja contem o erro. Isso que será impresso na tela.
                            return false;
                        }
                    }
                    else
                        {
                            ErroTabela erro = new ErroTabela(tabelaSimbolos.getLinha(), "Falta o return");
                            TabelaErros.getInstance().getTabelaErros().add(erro);// Tabela de Erros ja contem o erro. Isso que será impresso na tela.
                            return false;
                        }

                    if (tabelaSimbolos.reconhecer("}")) {
                        return true;
                    }
                    else
                        {
                            ErroTabela erro = new ErroTabela(tabelaSimbolos.getLinha(), "Falta um }");
                            TabelaErros.getInstance().getTabelaErros().add(erro);// Tabela de Erros ja contem o erro. Isso que será impresso na tela.
                            return false;
                        }

            }
            else
            {
                ErroTabela erro = new ErroTabela(tabelaSimbolos.getLinha(), "Deveria ser um identificador");
                TabelaErros.getInstance().getTabelaErros().add(erro);// Tabela de Erros ja contem o erro. Isso que será impresso na tela.
                return false;
            }
        }
        else
            {
                ErroTabela erro = new ErroTabela(tabelaSimbolos.getLinha(), "Deveria ser um identificador ou palavra reservada");
                TabelaErros.getInstance().getTabelaErros().add(erro);// Tabela de Erros ja contem o erro. Isso que será impresso na tela.
                return false;
            }

        return true;
    }


    public void recuperaErro(int posicao, TabelaSimbolos tabelaSimbolos, String descricaoErro, ArrayList<String> sincronizadores) {

        ErroTabela erro = new ErroTabela(tabelaSimbolos.getLinha(), descricaoErro);
        TabelaErros.getInstance().getTabelaErros().add(erro);// Tabela de Erros ja contem o erro. Isso que será impresso na tela.

        while(!isSincronizador(tabelaSimbolos.getLookahead(), tabelaSimbolos.getTipo(),sincronizadores)){
            tabelaSimbolos.soConsome();

        }

    }


    public boolean isSincronizador(String token, String tipo, ArrayList<String> sincronizadores){
        for(int i = 0 ; i < sincronizadores.size() ; i++){
            if(sincronizadores.get(i).equals(token) ||sincronizadores.get(i).equals(tipo) ){
                System.out.println("ENCONTROU UM SINCRONIZADOR " + sincronizadores.get(i));
                return true;
            }
        }
        return false;

    }

}
